﻿#include "XFFMpeg.h"
#include <QDebug>

/** 分数转double
 * @brief r2d
 * @param r
 * @return
 */
static double r2d(AVRational r)
{
    return r.den == 0 ? 0 : (double)r.num / (double)r.den; //den为分母
}

XFFMpeg::XFFMpeg()
{
    _errBuf[0]='\0';

    //1.av_register_all()：注册所有组件。
    av_register_all();
}

XFFMpeg::~XFFMpeg()
{
    close();
}

bool XFFMpeg::open(const char *path)
{
    close();

    QMutexLocker lock(&_mutex);
    {
        //2.avformat_open_input()：打开输入视频文件。
        int re = avformat_open_input(&_ic,path,0,0);
        if(re!=0)
        {//0为成功，非0打开错误
            av_strerror(re,_errBuf,sizeof (_errBuf));
            qDebug()<<_errBuf;
            return false;
        }

        //3.打开视频解码器
        for(int i=0; i < _ic->nb_streams; ++i) //判断流类别 比如视频流 音频流 字幕流
        {
            AVCodecContext *enc = _ic->streams[i]->codec;//解码上下文

            //判断为视频流
            if(enc->codec_type==AVMEDIA_TYPE_VIDEO)
            {
                _videoStream = i;

                //计算fps
                _fps = r2d(_ic->streams[i]->avg_frame_rate); //获得帧率

                AVCodec *codec = avcodec_find_decoder(enc->codec_id);//查找解码器
                if(!codec){//查找不到解码器
                    qDebug()<<"open codec failed!";
                    return false;
                }
                int err = avcodec_open2(enc,codec,NULL);
                if(err!=0){//未打开解码器
                    char buf[1024] = {0};
                    av_strerror(err,buf,sizeof (buf));
                    qDebug()<<buf;
                    return false;
                }
                qDebug()<<"open codec success! fps: "<<_fps;
            }

            //判断为音频流
            else if(enc->codec_type==AVMEDIA_TYPE_AUDIO)
            {
                _audioStream = i;
                AVCodec *codec = avcodec_find_decoder(enc->codec_id);//查找解码器
                if( avcodec_open2(enc,codec,NULL) < 0 ) return 0; //使用解码器打开封装

                _sampleRate = enc->sample_rate;
                _channel = enc->channels;
                _sampleFormat = enc->sample_fmt;
                switch (enc->sample_fmt) {
                case AV_SAMPLE_FMT_S16:
                    _sampleSize = 16;
                    break;
                case AV_SAMPLE_FMT_S32:
                    _sampleSize = 32;
                    break;
//                case AV_SAMPLE_FMT_FLTP:
//                    _sampleSize = 32;
                default:
                    break;
                }
                qDebug()<<QString("audio sampleRate: %1, sampleSize: %2, channels: %3").arg(_sampleRate).arg(_sampleSize).arg(_channel);
            }
        }

        _totalMs =  _ic->duration / (AV_TIME_BASE);
        qDebug()<<QString("file totalsec is %1:%2 duration: %3").arg(_totalMs/60).arg(_totalMs%60).arg(_ic->duration);
    }

    return true;
}

void XFFMpeg::close()
{
    QMutexLocker lock(&_mutex);

    if(_yuv) {av_frame_free(&_yuv);_yuv=Q_NULLPTR;}
    if(_cCtx) {sws_freeContext(_cCtx);_cCtx=Q_NULLPTR;}
    if(_aCtx) {swr_free(&_aCtx);_aCtx=Q_NULLPTR;}
    if(_ic) {avformat_close_input(&_ic);_ic=Q_NULLPTR;}

}

std::string XFFMpeg::getError()
{
    QMutexLocker lock(&_mutex);
    std::string re = this->_errBuf;
    return re;
}

AVPacket XFFMpeg::read()
{
    AVPacket pkt;
    memset(&pkt,0,sizeof(AVPacket));
    QMutexLocker lock(&_mutex);

    if (!_ic)
    {
        return pkt;
    }
    int err = av_read_frame(_ic, &pkt);//读取视频帧
    if (err != 0)//读取失败
    {
        av_strerror(err,_errBuf,sizeof(_errBuf));
    }

    return pkt;

}

int XFFMpeg::decode(const AVPacket *pkt)
{
    QMutexLocker lock(&_mutex);

    if(!_ic) return NULL;

    //申请解码的对象空间
    if(!_yuv) _yuv = av_frame_alloc();
    if(!_pcm) _pcm = av_frame_alloc();

    AVFrame* frame = _yuv;
    if(pkt->stream_index == _audioStream) frame = _pcm;

    //发送之前读取的视频帧pkt
    int re = avcodec_send_packet(_ic->streams[pkt->stream_index]->codec,pkt);
    if(re!=0){return NULL;}

    //解码pkt后存入frame中
    re = avcodec_receive_frame(_ic->streams[pkt->stream_index]->codec,frame);
    if(re!=0){return NULL;}

//    qDebug()<<"pts= "<<frame->pts;

    int p = frame->pts * r2d(_ic->streams[pkt->stream_index]->time_base);
    if(pkt->stream_index == _audioStream) _pts = p;
    return p;
}

bool XFFMpeg::toRGB(const AVFrame *yuv, char *out, int outwidth, int outheight)
{
    QMutexLocker lock(&_mutex);

    if(!_ic){_mutex.unlock();return false;}
    AVCodecContext* videoCtx = _ic->streams[this->_videoStream]->codec;
    _cCtx = sws_getCachedContext(_cCtx,
                                 videoCtx->width,
                                 videoCtx->height,
                                 videoCtx->pix_fmt,//输入像素格式
                                 outwidth,outheight,
                                 AV_PIX_FMT_BGRA,//输出格式
                                 SWS_BICUBIC,//转码算法
                                 NULL,NULL,NULL);
    if(!_cCtx){qDebug()<<"sws_getCachedContext failed!";return false;}

    uint8_t *data[AV_NUM_DATA_POINTERS] = {0};
    data[0] = (uint8_t*)out;
    int lineSize[AV_NUM_DATA_POINTERS] = {0};
    lineSize[0] = outwidth*4;//一行字节数 RGBA为4个字节

    int h = sws_scale(_cCtx,
                      _yuv->data,           //当前处理区域的每个通道数据指针
                      _yuv->linesize,       //每个通道行字节数
                      0, videoCtx->height,  //原视频帧的高度
                      data,                 //输出的每个通道数据指针
                      lineSize              //每个通道行字节数
                      );
//    if(h>0){qDebug()<<h;}

    return true;
}

int XFFMpeg::toPCM(char *out)
{
    QMutexLocker lock(&_mutex);
    if(!_ic || !_pcm || !out) return 0;

    AVCodecContext* audioCtx = _ic->streams[_audioStream]->codec;//音频解码器封装
    if(!_aCtx)
    {
        _aCtx = swr_alloc();
        swr_alloc_set_opts( _aCtx, audioCtx->channel_layout,
                            AV_SAMPLE_FMT_S16,
                            audioCtx->sample_rate,
                            audioCtx->channel_layout,
                            audioCtx->sample_fmt,
                            audioCtx->sample_rate,
                            0,0
                            );
        swr_init(_aCtx);
    }
    uint8_t *data[1];
    data[0] = (uint8_t*)out;

    //音频的重采样过程
    //return number of samples output per channel, negative value on error
    int len = swr_convert(_aCtx,data,
                          10000,
                          (const uint8_t**)_pcm->data,
                          _pcm->nb_samples
                          );
    if(len<=0) return 0;

    //计算音频占用的字节数的
    int outsize = av_samples_get_buffer_size(NULL,audioCtx->channels,
                                             _pcm->nb_samples,
                                             AV_SAMPLE_FMT_S16,
                                             0);
    return outsize;

}

